package com.wgo.bpot.client.servicerequest;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.logging.Logger;

import com.wgo.bpot.common.transport.exception.UserFailedAuthenticationException;
import com.wgo.bpot.common.transport.servicefacade.RemoteServices;
import com.wgo.bpot.domain.common.AuthenticationHandler;
import com.wgo.bpot.domain.common.Service;
import com.wgo.bpot.domain.common.UserCredential;
import com.wgo.bpot.domain.common.UserSession;
import com.wgo.bpot.domain.common.impl.ServiceImpl;

public class ServiceRequestor implements InvocationHandler{
    private UserSession userSession;
    private AuthenticationHandler authenticationHandler;
    private UserCredential userCredential;
    private RemoteServices remoteServices;
    private ExecutorService executor = Executors.newFixedThreadPool(100);
//    private XStream xStream = new XStream(new DomDriver());
    private static final Logger log = Logger.getLogger(ServiceRequestor.class.getSimpleName()); 

    @SuppressWarnings("unchecked")
    public static <S> S createServicesStub(RemoteServices remoteServices, Class<S> serviceApi, AuthenticationHandler authenticationHandler) {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        return (S)Proxy.newProxyInstance(loader, new Class[]{serviceApi,UserSessionContainer.class}, new ServiceRequestor(remoteServices,authenticationHandler));
    }
    
    @Deprecated
    public static <S> S createServicesStub(Class<S> serviceApi, InvocationHandler serviceRequestor){
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        return (S)Proxy.newProxyInstance(loader, new Class[]{serviceApi}, serviceRequestor);
    }

    public ServiceRequestor(RemoteServices remoteServices, AuthenticationHandler authenticationHandler) {
        this.remoteServices = remoteServices;
        this.authenticationHandler = authenticationHandler;
    }

    public void createSession() throws UserFailedAuthenticationException {
        if (null == userCredential) {
            this.userCredential = authenticationHandler.createUserCredential();
        }
        userSession = remoteServices.authenticateUser(userCredential);
        if (null == userSession) {
            log.warning("Invalid user: " + userCredential.getUserName());
            throw new UserFailedAuthenticationException(userCredential);
        }
    }

    public Object invokeService(Service service, Object[] args){
        Object result = null;
        if ((null == userSession) || (userSession.isExpired())) {
            createSession();
        }
        result = remoteServices.invokeService(userSession, service, args);
        return result;
    }

    public Future<Object> asynchronouslyInvokeService(final Service service, final Object[] args){
        Callable<Object> serviceCall = new Callable<Object>(){
            public Object call() throws Exception {
                return remoteServices.invokeService(userSession, service, args);
            }
        };
        return executor.submit(serviceCall);
    }

    public UserSession getUserSession() {
        return userSession;
    }

    public void setAuthenticationHandler(AuthenticationHandler authenticationHandler) {
        this.authenticationHandler = authenticationHandler;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (method.equals(UserSessionContainer.class.getMethod("getUserSession"))) {
            return userSession;
        }
        Object result = null;
        ServiceImpl service = new ServiceImpl();
        service.setName(method.getName());
        result = invokeService(service,args);
        return result;
    }


}
